package com.mazaiting.classfile.attr.entity;

import com.mazaiting.classfile.ClassReader;
import com.mazaiting.classfile.constant.ConstantPool;
import com.mazaiting.classfile.interfaces.Reader;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

import java.util.Arrays;

/**
 * 类型注解参数
 *
 * @author mazaiting
 * @since 2021/8/13
 */
public class TypeAnnotationEntity {
    /**
     * 目标类型 无符号 u1
     */
    private final short targetType;
    /**
     * 目标信息
     */
    private final TargetInfoEntity targetInfo;
    /**
     * 类型路径
     */
    private final TypePathEntity typePath;
    /**
     * 类型索引 无符号 u2
     */
    private final int typeIndex;
    /**
     * 节点值数量 无符号 u2
     */
    private final int numElementValuePairs;
    /**
     * 节点对信息
     */
    private final ElementValuePairsEntity[] elementValuePairsEntities;

    public TypeAnnotationEntity(ClassReader reader, ConstantPool pool) {
        this.targetType = reader.readU1ToUnsignedByte();
        this.targetInfo = TargetInfoEntity.readInfo(reader, targetType);
        this.typePath = new TypePathEntity(reader);
        this.typeIndex = reader.readU2ToUnsignedShort();
        this.numElementValuePairs = reader.readU2ToUnsignedShort();
        this.elementValuePairsEntities = new ElementValuePairsEntity[numElementValuePairs];
        for (int i = 0; i < numElementValuePairs; i++) {
            this.elementValuePairsEntities[i] = new ElementValuePairsEntity(reader, pool);
        }
    }

    @Override
    public String toString() {
        return "TypeAnnotationEntity{" +
                "targetType=" + targetType +
                ", targetInfo=" + targetInfo +
                ", typePath=" + typePath +
                ", typeIndex=" + typeIndex +
                ", numElementValuePairs=" + numElementValuePairs +
                ", elementValuePairsEntities=" + Arrays.toString(elementValuePairsEntities) +
                '}';
    }

    @ToString
    private static abstract class TargetInfoEntity implements Reader {
        /**
         * 目标类型 无符号 u1
         */
        protected final short targetType;

        protected TargetInfoEntity(short targetType) {
            this.targetType = targetType;
        }

        /**
         * 读取信息
         *
         * @param reader     类读取器
         * @param targetType 目标类型
         * @return 返回目标信息实体
         */
        public static TargetInfoEntity readInfo(ClassReader reader, short targetType) {
            TargetInfoEntity entity;
            if (targetType == 0 || targetType == 1) {
                entity = new TypeParameterTarget(targetType);
            } else if (targetType == 16) {
                entity = new SupertypeTarget(targetType);
            } else if (targetType == 17 || targetType == 18) {
                entity = new TypeParameterBoundTarget(targetType);
            } else if (targetType == 19 || targetType == 20 || targetType == 21) {
                entity = new EmptyTarget(targetType);
            } else if (targetType == 22) {
                entity = new FormalParameterTarget(targetType);
            } else if (targetType == 23) {
                entity = new ThrowsTarget(targetType);
            } else if (targetType == 64 || targetType == 65) {
                entity = new LocalVarTarget(targetType);
            } else if (targetType == 66) {
                entity = new CatchTarget(targetType);
            } else if (targetType == 67 || targetType == 68 || targetType == 69 || targetType == 70) {
                entity = new OffsetTarget(targetType);
            } else if (targetType == 71 || targetType == 72 || targetType == 73 || targetType == 74 || targetType == 75) {
                entity = new TypeArgumentTarget(targetType);
            } else {
                throw new RuntimeException("未知 target_type: " + targetType);
            }
            entity.readInfo(reader);
            return entity;
        }

        private static class TypeParameterTarget extends TargetInfoEntity {
            /**
             * 类型参数索引 u1
             */
            private short typeParameterIndex;

            protected TypeParameterTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.typeParameterIndex = reader.readU1ToUnsignedByte();
            }

            @Override
            public String toString() {
                return "TypeParameterTarget{" +
                        "targetType=" + targetType +
                        ", typeParameterIndex=" + typeParameterIndex +
                        '}';
            }
        }

        private static class SupertypeTarget extends TargetInfoEntity {
            /**
             * 超类类型索引 u2
             */
            private int supertypeIndex;

            public SupertypeTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.supertypeIndex = reader.readU2ToUnsignedShort();
            }

            @Override
            public String toString() {
                return "SupertypeTarget{" +
                        "targetType=" + targetType +
                        ", supertypeIndex=" + supertypeIndex +
                        '}';
            }
        }

        private static class TypeParameterBoundTarget extends TargetInfoEntity {
            /**
             * 类型参数索引 u1
             */
            private short typeParameterIndex;
            /**
             * 索引 u1
             */
            private short boundIndex;

            public TypeParameterBoundTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.typeParameterIndex = reader.readU1ToUnsignedByte();
                this.boundIndex = reader.readU1ToUnsignedByte();
            }

            @Override
            public String toString() {
                return "TypeParameterBoundTarget{" +
                        "targetType=" + targetType +
                        ", typeParameterIndex=" + typeParameterIndex +
                        ", boundIndex=" + boundIndex +
                        '}';
            }
        }

        private static class EmptyTarget extends TargetInfoEntity {

            public EmptyTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {

            }
        }

        private static class FormalParameterTarget extends TargetInfoEntity {
            /**
             * 参数索引 u1
             */
            private short formalParameterIndex;

            public FormalParameterTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.formalParameterIndex = reader.readU1ToUnsignedByte();
            }

            @Override
            public String toString() {
                return "FormalParameterTarget{" +
                        "targetType=" + targetType +
                        ", formalParameterIndex=" + formalParameterIndex +
                        '}';
            }
        }

        @ToString(callSuper = true)
        private static class ThrowsTarget extends TargetInfoEntity {
            /**
             * 异常类型索引 u2
             */
            private int throwsTypeIndex;

            public ThrowsTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.throwsTypeIndex = reader.readU2ToUnsignedShort();
            }
        }

        private static class LocalVarTarget extends TargetInfoEntity {
            /**
             * 表长度 u2
             */
            private int tableLength;
            /**
             * 表
             */
            private Table[] tables;

            public LocalVarTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.tableLength = reader.readU2ToUnsignedShort();
                this.tables = new Table[tableLength];
                for (int i = 0; i < tableLength; i++) {
                    this.tables[i] = new Table(
                            reader.readU2ToUnsignedShort(),
                            reader.readU2ToUnsignedShort(),
                            reader.readU2ToUnsignedShort()
                    );
                }
            }

            @Override
            public String toString() {
                return "LocalVarTarget{" +
                        "targetType=" + targetType +
                        ", tableLength=" + tableLength +
                        ", tables=" + Arrays.toString(tables) +
                        '}';
            }

            @Data
            @AllArgsConstructor
            private static class Table {
                /**
                 * 开始计数 u2
                 */
                private int startPc;
                /**
                 * 长度 u2
                 */
                private int length;
                /**
                 * 索引 u2
                 */
                private int index;
            }
        }

        @ToString(callSuper = true)
        private static class CatchTarget extends TargetInfoEntity {
            /**
             * 异常表索引 u2
             */
            private int exceptionTableIndex;

            protected CatchTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.exceptionTableIndex = reader.readU2ToUnsignedShort();
            }
        }

        @ToString(callSuper = true)
        private static class OffsetTarget extends TargetInfoEntity {
            /**
             * 偏移 u2
             */
            private int offset;

            protected OffsetTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.offset = reader.readU2ToUnsignedShort();
            }
        }

        @ToString(callSuper = true)
        private static class TypeArgumentTarget extends TargetInfoEntity {
            /**
             * 偏移 u2
             */
            private int offset;
            /**
             * 类型参数索引 u1
             */
            private short typeArgumentIndex;

            protected TypeArgumentTarget(short targetType) {
                super(targetType);
            }

            @Override
            public void readInfo(ClassReader reader) {
                this.offset = reader.readU2ToUnsignedShort();
                this.typeArgumentIndex = reader.readU1ToUnsignedByte();
            }
        }
    }

    private static class TypePathEntity {
        /**
         * 路径长度 u1
         */
        private final short pathLength;
        /**
         * 路径
         */
        private final Path[] paths;

        public TypePathEntity(ClassReader reader) {
            this.pathLength = reader.readU1ToUnsignedByte();
            this.paths = new Path[pathLength];
            for (int i = 0; i < pathLength; i++) {
                this.paths[i] = new Path(
                        reader.readU1ToUnsignedByte(),
                        reader.readU1ToUnsignedByte()
                );
            }
        }

        @Override
        public String toString() {
            return "TypePathEntity{" +
                    "pathLength=" + pathLength +
                    ", paths=" + Arrays.toString(paths) +
                    '}';
        }

        @Data
        @AllArgsConstructor
        private static class Path {
            /**
             * 路径分类 u1
             */
            private short typePathKind;
            /**
             * 类型参数索引 u1
             */
            private short typeArgumentIndex;
        }

    }
}


















